package controller

import (
	"context"
	"fmt"
	"github.com/go-logr/logr"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	v1 "schedulerNetAwareController/api/cic.io/v1"
	"schedulerNetAwareController/pb"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	latencyListSizeBound = 5
)

type PingLatencyServer struct {
	pb.UnimplementedNodeTopoNetMetricServiceServer
	clusterClient client.Client
	logger        *logr.Logger
}

func NewPingLatencyServer(client client.Client, logger *logr.Logger) *PingLatencyServer {
	return &PingLatencyServer{
		clusterClient: client,
		logger:        logger,
	}
}

func (server *PingLatencyServer) UploadPingLatency(ctx context.Context, metric *pb.PingLatencyMetric) (*pb.Response, error) {
	logger := server.logger
	nodeName, toNodes := metric.NodeName, metric.ToNodes

	key := types.NamespacedName{
		Name:      nodeName + "-metric",
		Namespace: NodeTopoNetMetricNamespace,
	}
	nodeTopoNetMetric := &v1.NodeTopoNetMetric{}
	err := server.clusterClient.Get(ctx, key, nodeTopoNetMetric)
	if err != nil {
		if errors.IsNotFound(err) {
			logger.Error(err, "未找到对应的NodeTopoNetMetric资源", "key", key)
			return errorResponseNotFount(), err
		}
	}

	for i, toNode := range toNodes {
		metrics := nodeTopoNetMetric.Status.Latencies.LatencyMetrics
		idx := findTargetInList(metrics, toNode)
		if idx == -1 {
			logger.Error(fmt.Errorf("未找到对应的node"), "[UploadPingLatency]-未找到对应的node", "toNode", toNode)
			continue
		}
		metrics[idx].Latency = updateLatency(metrics[idx].Latency, metric.Latencies[i])
		nodeTopoNetMetric.Status.Latencies.LatencyMetrics = metrics
	}
	err = server.clusterClient.Status().Update(ctx, nodeTopoNetMetric)
	if err != nil {
		logger.Error(err, "更新nodeTopoNetMetric失败")
		return &pb.Response{
			Msg: "update failed",
		}, err
	}

	return &pb.Response{
		Msg: "update successfully",
	}, nil
}

func errorResponseNotFount() *pb.Response {
	return &pb.Response{Msg: "未找到对应的nodeTopoNetMetric"}
}

func findTargetInList(list []v1.LatencyToNode, target string) (index int) {
	for i, item := range list {
		if item.NodeName == target {
			return i
		}
	}
	return -1
}

func updateLatency(origin []string, newLatency string) []string {
	// 保证切片大小适中小于等于latencyListSizeBound
	if len(origin) < latencyListSizeBound {
		return append(origin, newLatency)
	} else {
		return append(origin[1:], newLatency)
	}
}
